package sudoku_gui_handler;

import help_util.IndexConverter;
import model_controller.ControlLayer;
import sudoku_gui.*;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import its_models.student_model.StudentModelProperties;
import javax.swing.JButton;
import sudoku_gameboard_structure.SudokuGameboard;
import sudoku_properties.AlertBoxParam;
import sudoku_properties.BasicSudokuParam;
import sudoku_properties.SudokuGameInfoParam;

/*********************************************************************
 * Class DnDHandler
 * 1. handle the dnd event
 * 2. trigger the expert model to do checking
 *********************************************************************/
public class DnDHandler {

    public DnDHandler(){
        //modelsBridge = new ModelsBridge();
    }

    public DnDHandler(JLabel[][] inputCellsValueLabel, JLabel[][][] inputCaluCellsLabel, SudokuGameboard inputGameboard,
            ControlLayer inputController, JButton inputUndoButton, JButton inputRedoButton){

        cellsValueLabel = inputCellsValueLabel;
        caluCellsLabel = inputCaluCellsLabel;
        /* default DND enabled */
        isDnDEnabled = SudokuComponentsProperties.getIsTrue();
        isEnableDNDCellLabel = new boolean[cellsValueLabel.length][];
        /* default all DND targets are allowed to drop */
        for(int i=0; i<isEnableDNDCellLabel.length; i++){
            isEnableDNDCellLabel[i] = new boolean[cellsValueLabel[i].length];
            for(int j=0; j<isEnableDNDCellLabel[i].length; j++){
                 isEnableDNDCellLabel[i][j] = SudokuComponentsProperties.getIsTrue();
            }
        }
        /* set up gameboard */
        Gameboard = inputGameboard;
        Controller = inputController;
        undoStepsButton = inputUndoButton;
        redoStepsButton = inputRedoButton;

        LangMode = Controller.getLangMode();
    }

    public void setupDnD(JLabel[] inputDragLabelArray, JLayeredPane inputDropPanel){
        dragLabelArray= inputDragLabelArray;
        dropPanel = inputDropPanel;

        DragSource dragSource = DragSource.getDefaultDragSource();
        for(int i=0; i<dragLabelArray.length; i++){
            setupDragSourceListener();
            setupDragGestureListener(dragLabelArray[i]);
            dragSource.createDefaultDragGestureRecognizer(dragLabelArray[i], DnDConstants.ACTION_COPY, dragGestureListener);
        }
        setupDropTargetListener();
        new DropTarget(dropPanel, dropTargetListener);

    }
    public void setupDragSourceListener(){
        dragSourceListener = new DragSourceListener() {

            public void dragEnter(DragSourceDragEvent dsde) {
            }

            public void dragOver(DragSourceDragEvent dsde) {
            }

            public void dropActionChanged(DragSourceDragEvent dsde) {
            }

            public void dragExit(DragSourceEvent dse) {
            }

            public void dragDropEnd(DragSourceDropEvent dsde) {
            }
        };
    }
    public void setupDragGestureListener(final JLabel label){
        dragGestureListener = new DragGestureListener() {

            public void dragGestureRecognized(DragGestureEvent dge) {
                 Transferable transferable = new LabelTransferable(label.getText());
                 dge.startDrag(null, transferable, dragSourceListener);
            }
        };
    }
    public void setupDropTargetListener(){
        dropTargetListener = new DropTargetListener() {

            public void dragEnter(DropTargetDragEvent dtde) {
            }

            public void dragOver(DropTargetDragEvent dtde) {
            }

            public void dropActionChanged(DropTargetDragEvent dtde) {
            }

            public void dragExit(DropTargetEvent dte) {
            }

            public void drop(DropTargetDropEvent dtde) {
                dtde.acceptDrop(DnDConstants.ACTION_COPY);
                Transferable transferable = dtde.getTransferable();
                DataFlavor[] flavors = transferable.getTransferDataFlavors();
                for (int i=0; i<flavors.length; i++){
                    DataFlavor d = flavors[i];
                    if(d.equals(DataFlavor.stringFlavor)){
                        String data;
                        try {
                            data = (String) transferable.getTransferData(d);
                            Point dropLoc = dtde.getLocation();
                            triggerDnD(dropLoc, data);
                        } catch (UnsupportedFlavorException ex) {
                            Logger.getLogger(DnDHandler.class.getName()).log(Level.SEVERE, null, ex);
                        } catch (IOException ex) {
                            Logger.getLogger(DnDHandler.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
            }
        };
    }

    public void triggerDnD(Point inputDropLoc, String inputDropData){
        final Point dropLoc=inputDropLoc;
        String dropData=inputDropData;
        String actionCommand;
        int dropDataValue = 0;
        final int indexXonPanel = (int) dropLoc.getX();
        final int indexYonPanel = (int) dropLoc.getY();
        final int dropIndexX = indexXonPanel / SIZE_CELL -1 ;
        final int dropIndexY = indexYonPanel / SIZE_CELL -1 ;
        /* do not trigger any given or ans correctly cells */
        if(isDnDEnabled && isEnableDNDCellLabel[dropIndexX][dropIndexY]){
            String eraseStr = "";
            if (LangMode == ENG_MODE){
                eraseStr = "Erase";
            }
            if (LangMode == CHIN_MODE){
                eraseStr = "擦掉";
            }
            if (dropData.equalsIgnoreCase(eraseStr)){
                dropData = "";
                dropDataValue = Integer.parseInt(cellsValueLabel[dropIndexX][dropIndexY].getText());
                actionCommand = StudentModelProperties.getEraseValueCommand();
            }else{
                 dropDataValue = Integer.parseInt(dropData);
                 actionCommand = StudentModelProperties.getAssignValueCommand();
            }
            /* To be improve ... */
            /* if - get the same input to same cells */
            /* else if - get if input a number to a cells with value */
            /* else - all valid drop data */
            if( cellsValueLabel[dropIndexX][dropIndexY].getText().equalsIgnoreCase(dropData)){
                alertDuplicateUserInput();
            }else if(!cellsValueLabel[dropIndexX][dropIndexY].getText().equalsIgnoreCase("") && !dropData.equalsIgnoreCase("")){
                alertInvalidUserInput();
            }else if( dropData.equalsIgnoreCase("") && !Controller.isLearnerInputCorrect(dropIndexX, dropIndexY, 0)){
                alertEraseCorrectInput();
            }else{
                /* UI Step 1 : Update UI*/
                cellsValueLabel[dropIndexX][dropIndexY].setText(dropData.toString());
                if(!dropData.equalsIgnoreCase("")){
                    for(int i=0; i<NUM_CELLS_IN_UNIT; i++){
                        Controller.setVisibleCaluCellsLabel(dropIndexX, dropIndexY, i, SudokuComponentsProperties.getIsFalse());
                    }
                    Controller.setTextAssignCellValue(dropIndexX, dropIndexY, dropDataValue);
                }else{
                    for(int i=0; i<NUM_CELLS_IN_UNIT; i++){
                        /* check if user check is possible before define the definite value */
                        Controller.setVisibleCaluCellsLabel(dropIndexX, dropIndexY, i, Gameboard.isPossCellValue(dropIndexX, dropIndexY, i+1));
                    }
                    Controller.setTextEraseCellValue(dropIndexX, dropIndexY, dropDataValue);
                }

                /* UI Step 2 : update gameboard */
                if(!dropData.equalsIgnoreCase("")){
                    Gameboard.setCellValue(dropIndexX, dropIndexY, dropDataValue);
                }else{
                    Gameboard.setCellValue(dropIndexX, dropIndexY, 0);
                }

                /* TUTOR Step 1 : check answer  - only check for define an answer */
                boolean isInputCorrect;
                if(!dropData.equalsIgnoreCase("")){
                    isInputCorrect = Controller.isLearnerInputCorrect(dropIndexX, dropIndexY, dropDataValue);
                }
                else{
                    isInputCorrect = Controller.isLearnerInputCorrect(dropIndexX, dropIndexY, 0);
                }

                final boolean isInputAllCorrect = Controller.isLearnerInputAllCorrect();
                if(isInputAllCorrect){
                    if(Controller.getLangMode() == ENG_MODE){
                        Controller.setTextGameStatus(GAME_STATUS_CORRECT);
                    }
                    if(Controller.getLangMode() == CHIN_MODE){
                        Controller.setTextGameStatus(GAME_STATUS_CORRECT_IN_CHIN);
                    }
                }else{
                    if(Controller.getLangMode() == ENG_MODE){
                        Controller.setTextGameStatus(GAME_STATUS_WRONG);
                    }
                    if(Controller.getLangMode() ==CHIN_MODE){
                        Controller.setTextGameStatus(GAME_STATUS_WRONG_IN_CHIN);
                    }
                }

                if(isInputCorrect){
                    /* EXPERT Step 1 : update user input with expert  - includes ruleUsed*/
                     // 20091029 phrase2 start
                     if(!dropData.equalsIgnoreCase("")){
                         
                         // 20091112 phrase2 start
                         Controller.setCellValueBySystem(dropIndexX, dropIndexY, dropDataValue, actionCommand, false);

                         /* convert the below into setCellValueBySystm function */

                             // Controller.setCellDefiniteValue(dropIndexX, dropIndexY, dropDataValue);
                             /* set all the rules possibility, especially for store in DB  */
                             /*- must place before apply logical rules, else rules foudn are updated*/
                             // Controller.setRuleUsed(dropIndexX, dropIndexY, Controller.getAllRules(dropIndexX, dropIndexY));
                             //Controller.setTextLearnerStepsMsgBox("\n"+dropIndexX+","+dropIndexY+" with rules "+  Controller.getAllRules(dropIndexX, dropIndexY) );
                             // 20091018 phrase2 end

                             // 20091017 phrase2 start - to be improve..
                             // Controller.setTextSolvingStatus(Controller.getNumCellsUnsolved());
                             //  Controller.setupStepsMatrix(Controller.applyLogicalRules());

                             /* UI Step 3 : Update Undo & Redo stack */
                             // int index = IndexConverter.getIndex(dropIndexX, dropIndexY);
                             // Controller.recordSteps(index, dropDataValue, actionCommand);

                             // 20091017 phrase2 end

                             /* before solve the puzzle, stepsMatrix should be store */
                             // Controller.solvePuzzle();
                             // Controller.reocrdStepsMatrix(Controller.getCorrectStepsCount());

                             // 20091112 phrase2 start
                             // Controller.reStartAutoHintsTimer(-1);
                             // 20091112 phrase2 end
                             /* For testing */
                             //if(Controller.getNumCellsUnsolved()== 0){
                                /* To be Improve */
                             //  Controller.endGame();
                             //}

                         // 20091112 phrase2 end
                     }
                     // 20091029 phrase2 end
                }else{
                    int value = Controller.getMisconceptionNum();
                    Controller.setMisconceptionNum(value+1);
                }
                /* UI Step 4 : SetupButton */
                // 20091029 phrase2 start - cancel Undo Redu Button
                //redoStepsButton.setEnabled(Controller.isRedoEnabled());
                //undoStepsButton.setEnabled(Controller.isUndoEnabled());
                // 20091029 phrase2 end - cancel Undo Redu Button
            }
        }
    }

    /* This function only show user sudoku */


    class LabelTransferable implements Transferable {
        public LabelTransferable(String inputvalue) {
            Value =inputvalue;
        }
        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }
        private DataFlavor[] flavors = { DataFlavor.stringFlavor };
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return true;
        }
        public synchronized Object getTransferData(DataFlavor flavor)
        throws UnsupportedFlavorException {
            if (flavor.equals(DataFlavor.stringFlavor)) {
                return Value;
            } else {
                throw new UnsupportedFlavorException(flavor);
            }
        }

        private String Value;
    }

    /* set if DnD function is enable*/
    public void isDNDFunctionEnable(boolean isEnabled){
       isDnDEnabled = isEnabled;
    }
    public boolean getIsDNDFunctionEnable(){
        return isDnDEnabled;
    }
    public void isDropTargetEnabled(int indexX, int indexY, boolean isEnabled){
        isEnableDNDCellLabel[indexX][indexY] = isEnabled;
    }
    public boolean getIsDropTargetEnabled(int indexX, int indexY, boolean isEnabled){
        return isEnableDNDCellLabel[indexX][indexY];
    }

    public void alertDuplicateUserInput(){
        String DuplicateUserInputAlertTitle = "";
        String DuplicateUserInputAlertMsg ="";
        if (LangMode == ENG_MODE){
            DuplicateUserInputAlertTitle = AlertBoxParam.getDuplicateUserInputAlertTitle();
            DuplicateUserInputAlertMsg = AlertBoxParam.getDuplicateUserInputAlertMsg();
        }
        if (LangMode == CHIN_MODE){
            DuplicateUserInputAlertTitle = AlertBoxParam.getDuplicateUserInputAlertTitleInChin();
            DuplicateUserInputAlertMsg = AlertBoxParam.getDuplicateUserInputAlertMsgInChin();
        }
        JOptionPane.showConfirmDialog(null, DuplicateUserInputAlertMsg, DuplicateUserInputAlertTitle, JOptionPane.YES_OPTION);
    }

    public void alertInvalidUserInput(){
        String InvalidUserInputAlertTitle = "";
        String InvalidUserInputAlertMsg = "";
        if (LangMode == ENG_MODE){
            InvalidUserInputAlertTitle = AlertBoxParam.getInvalidUserInputAlertTitle();
            InvalidUserInputAlertMsg = AlertBoxParam.getInvalidUserInputAlertMsg();
        }
        if (LangMode == CHIN_MODE){
            InvalidUserInputAlertTitle = AlertBoxParam.getInvalidUserInputAlertTitleInChin();
            InvalidUserInputAlertMsg = AlertBoxParam.getInvalidUserInputAlertMsgInChin();
        }
        JOptionPane.showConfirmDialog(null, InvalidUserInputAlertMsg, InvalidUserInputAlertTitle, JOptionPane.YES_OPTION);
    }

    public void alertEraseCorrectInput(){
        String EraseCorrectInputAlertTitle = "";
        String EraseCorrectInputAlertMsg = "";
        if (LangMode == ENG_MODE){
            EraseCorrectInputAlertTitle = AlertBoxParam.getEraseCorrectInputAlertTitle();
            EraseCorrectInputAlertMsg = AlertBoxParam.getEraseCorrectInputAlertMsg();
        }
        if (LangMode == CHIN_MODE){
            EraseCorrectInputAlertTitle = AlertBoxParam.getEraseCorrectInputAlertTitleInChin();
            EraseCorrectInputAlertMsg = AlertBoxParam.getEraseCorrectInputAlertMsgInChin();
        }
        JOptionPane.showConfirmDialog(null, EraseCorrectInputAlertMsg, EraseCorrectInputAlertTitle, JOptionPane.YES_OPTION);
    }


    public DragGestureListener dragGestureListener;
    public DropTargetListener dropTargetListener;
    public DragSourceListener dragSourceListener;

    //20100112
    private int LangMode;

    /* Drag and Drop objects*/
    private JLabel[] dragLabelArray;
    private JLayeredPane dropPanel;
    private JLabel[][] cellsValueLabel;
    private JLabel[][][] caluCellsLabel;

    /* Allowance of the cells value */
    private boolean[][] isEnableDNDCellLabel;

    /* For Whole DND Event */
    private boolean isDnDEnabled;

    /* local variables */
    private final int SIZE_CELL = SudokuComponentsProperties.getSizeCell();
    private final int NUM_CELLS_IN_UNIT = BasicSudokuParam.getNumCellsInUnit();

    private SudokuGameboard Gameboard;
    private ControlLayer Controller;

    private JButton undoStepsButton;
    private JButton redoStepsButton;


    /* Constant variables */
    final String GAME_STATUS_CORRECT = SudokuGameInfoParam.getGameStatusCorrrect();
    final String GAME_STATUS_CORRECT_IN_CHIN = SudokuGameInfoParam.getGameStatusCorrrectInChin();
    final String GAME_STATUS_WRONG = SudokuGameInfoParam.getGameStatusWrong();
    final String GAME_STATUS_WRONG_IN_CHIN = SudokuGameInfoParam.getGameStatusWrongInChin();
    /* SudokuGameInfoParam */
    final int ENG_MODE = SudokuGameInfoParam.getEnglishMode();
    final int CHIN_MODE = SudokuGameInfoParam.getChineseMode();

}
